//! 表达式相关

use std::ops::{Add, BitAnd, BitOr};
use time::macros::format_description;

use crate::model::{
    field::Field,
    relation::JoinInfo,
    types::{OrmDate, OrmDateTime, OrmTime},
};

use super::method::QueryMethod;

#[derive(Debug, PartialEq)]
pub enum QueryExprRightValue<'a> {
    /// 字符串
    Str(&'a str),
    /// 字符
    Char(char),
    /// u8 整数
    U8(u8),
    /// u16 整数
    U16(u16),
    /// u32 整数
    U32(u32),
    /// u64 整数
    U64(u64),
    /// i8 整数
    I8(i8),
    /// i16 整数
    I16(i16),
    /// i32 整数
    I32(i32),
    /// i64 整数
    I64(i64),
    /// f32 浮点数
    F32(f32),
    /// f64 浮点
    F64(f64),
    /// 字符串列表
    SliceStr(&'a [&'a str]),
    /// 字符列表
    SliceChar(&'a [char]),
    /// u8 整数列表
    SliceU8(&'a [u8]),
    /// u16 整数列表
    SliceU16(&'a [u16]),
    /// u32 整数列表
    SliceU32(&'a [u32]),
    /// u64 整数列表
    SliceU64(&'a [u64]),
    /// i8 整数列表
    SliceI8(&'a [i8]),
    /// i16 整数列表
    SliceI16(&'a [i16]),
    /// i32 整数列表
    SliceI32(&'a [i32]),
    /// i64 整数列表
    SliceI64(&'a [i64]),
    /// f32 浮点数列表
    SliceF32(&'a [f32]),
    /// u8 整数列表
    SliceF64(&'a [f64]),
    /// 日期
    Date(OrmDate),
    /// 时间
    Time(OrmTime),
    /// 日期和时间
    DateTime(OrmDateTime),
    /// 日期列表
    SliceDate(&'a [OrmDate]),
    /// 时间列表
    SliceTime(&'a [OrmTime]),
    /// 空
    Null,
    /// 布尔值
    Bool(bool),
}

impl<'a> From<&'a str> for QueryExprRightValue<'a> {
    fn from(value: &'a str) -> Self {
        Self::Str(value)
    }
}
impl From<char> for QueryExprRightValue<'_> {
    fn from(value: char) -> Self {
        Self::Char(value)
    }
}
impl From<u8> for QueryExprRightValue<'_> {
    fn from(value: u8) -> Self {
        Self::U8(value)
    }
}
impl From<u16> for QueryExprRightValue<'_> {
    fn from(value: u16) -> Self {
        Self::U16(value)
    }
}
impl From<u32> for QueryExprRightValue<'_> {
    fn from(value: u32) -> Self {
        Self::U32(value)
    }
}
impl From<u64> for QueryExprRightValue<'_> {
    fn from(value: u64) -> Self {
        Self::U64(value)
    }
}
impl From<i8> for QueryExprRightValue<'_> {
    fn from(value: i8) -> Self {
        Self::I8(value)
    }
}
impl From<i16> for QueryExprRightValue<'_> {
    fn from(value: i16) -> Self {
        Self::I16(value)
    }
}
impl From<i32> for QueryExprRightValue<'_> {
    fn from(value: i32) -> Self {
        Self::I32(value)
    }
}
impl From<i64> for QueryExprRightValue<'_> {
    fn from(value: i64) -> Self {
        Self::I64(value)
    }
}
impl From<f32> for QueryExprRightValue<'_> {
    fn from(value: f32) -> Self {
        Self::F32(value)
    }
}
impl From<f64> for QueryExprRightValue<'_> {
    fn from(value: f64) -> Self {
        Self::F64(value)
    }
}
impl<'a> From<&'a [&'a str]> for QueryExprRightValue<'a> {
    fn from(value: &'a [&'a str]) -> Self {
        Self::SliceStr(value)
    }
}
impl<'a> From<&'a [char]> for QueryExprRightValue<'a> {
    fn from(value: &'a [char]) -> Self {
        Self::SliceChar(value)
    }
}
impl<'a> From<&'a [u8]> for QueryExprRightValue<'a> {
    fn from(value: &'a [u8]) -> Self {
        Self::SliceU8(value)
    }
}
impl<'a> From<&'a [u16]> for QueryExprRightValue<'a> {
    fn from(value: &'a [u16]) -> Self {
        Self::SliceU16(value)
    }
}
impl<'a> From<&'a [u32]> for QueryExprRightValue<'a> {
    fn from(value: &'a [u32]) -> Self {
        Self::SliceU32(value)
    }
}
impl<'a> From<&'a [u64]> for QueryExprRightValue<'a> {
    fn from(value: &'a [u64]) -> Self {
        Self::SliceU64(value)
    }
}
impl<'a> From<&'a [i8]> for QueryExprRightValue<'a> {
    fn from(value: &'a [i8]) -> Self {
        Self::SliceI8(value)
    }
}
impl<'a> From<&'a [i16]> for QueryExprRightValue<'a> {
    fn from(value: &'a [i16]) -> Self {
        Self::SliceI16(value)
    }
}
impl<'a> From<&'a [i32]> for QueryExprRightValue<'a> {
    fn from(value: &'a [i32]) -> Self {
        Self::SliceI32(value)
    }
}
impl<'a> From<&'a [i64]> for QueryExprRightValue<'a> {
    fn from(value: &'a [i64]) -> Self {
        Self::SliceI64(value)
    }
}
impl<'a> From<&'a [f32]> for QueryExprRightValue<'a> {
    fn from(value: &'a [f32]) -> Self {
        Self::SliceF32(value)
    }
}
impl<'a> From<&'a [f64]> for QueryExprRightValue<'a> {
    fn from(value: &'a [f64]) -> Self {
        Self::SliceF64(value)
    }
}
impl From<OrmDate> for QueryExprRightValue<'_> {
    fn from(value: OrmDate) -> Self {
        Self::Date(value)
    }
}

impl From<OrmTime> for QueryExprRightValue<'_> {
    fn from(value: OrmTime) -> Self {
        Self::Time(value)
    }
}

impl From<OrmDateTime> for QueryExprRightValue<'_> {
    fn from(value: OrmDateTime) -> Self {
        Self::DateTime(value)
    }
}
impl From<bool> for QueryExprRightValue<'_> {
    fn from(value: bool) -> Self {
        Self::Bool(value)
    }
}
impl QueryExprRightValue<'_> {
    /// 转换为sql格式数据
    pub fn to_sql(&self) -> String {
        match self {
            QueryExprRightValue::Str(value) => format!("{}", value),
            QueryExprRightValue::Char(value) => format!("{}", value),
            QueryExprRightValue::U8(value) => format!("{value}"),
            QueryExprRightValue::U16(value) => format!("{value}"),
            QueryExprRightValue::U32(value) => format!("{value}"),
            QueryExprRightValue::U64(value) => format!("{value}"),
            QueryExprRightValue::I8(value) => format!("{value}"),
            QueryExprRightValue::I16(value) => format!("{value}"),
            QueryExprRightValue::I32(value) => format!("{value}"),
            QueryExprRightValue::I64(value) => format!("{value}"),
            QueryExprRightValue::F32(value) => format!("{value}"),
            QueryExprRightValue::F64(value) => format!("{value}"),
            QueryExprRightValue::SliceStr(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| format!("'{item}'"))
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceChar(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| format!("'{item}'"))
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceU8(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceU16(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceU32(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceU64(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceI8(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceI16(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceI32(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceI64(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceF32(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceF64(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::Date(value) => {
                let format = format_description!("[year]-[month]-[day]");
                format!("{}", value.format(format).unwrap())
            }
            QueryExprRightValue::Time(value) => {
                let format = format_description!("[hour]:[minute]:[second]");
                format!("{}", value.format(format).unwrap())
            }
            QueryExprRightValue::DateTime(value) => {
                let format = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
                format!("{}", value.format(format).unwrap())
            }
            QueryExprRightValue::Null => "NULL".into(),
            QueryExprRightValue::Bool(value) => {
                if *value {
                    "TRUE".into()
                } else {
                    "FALSE".into()
                }
            }
            QueryExprRightValue::SliceDate(value) => {
                let format = format_description!("[year]-[month]-[day]");
                format!(
                    "({})",
                    value
                        .iter()
                        .map(|item| item.format(format).unwrap())
                        .collect::<Vec<String>>()
                        .join(",")
                )
            }
            QueryExprRightValue::SliceTime(value) => {
                let format = format_description!("[hour]-[minute]-[second]");
                format!(
                    "({})",
                    value
                        .iter()
                        .map(|item| item.format(format).unwrap())
                        .collect::<Vec<String>>()
                        .join(",")
                )
            }
        }
    }
    pub fn to_(&self) -> String {
        match self {
            QueryExprRightValue::Str(value) => format!("{}", value),
            QueryExprRightValue::Char(value) => format!("{}", value),
            QueryExprRightValue::U8(value) => format!("{value}"),
            QueryExprRightValue::U16(value) => format!("{value}"),
            QueryExprRightValue::U32(value) => format!("{value}"),
            QueryExprRightValue::U64(value) => format!("{value}"),
            QueryExprRightValue::I8(value) => format!("{value}"),
            QueryExprRightValue::I16(value) => format!("{value}"),
            QueryExprRightValue::I32(value) => format!("{value}"),
            QueryExprRightValue::I64(value) => format!("{value}"),
            QueryExprRightValue::F32(value) => format!("{value}"),
            QueryExprRightValue::F64(value) => format!("{value}"),
            QueryExprRightValue::SliceStr(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| format!("'{item}'"))
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceChar(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| format!("'{item}'"))
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceU8(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceU16(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceU32(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceU64(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceI8(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceI16(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceI32(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceI64(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceF32(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::SliceF64(value) => format!(
                "({})",
                value
                    .iter()
                    .map(|item| item.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            ),
            QueryExprRightValue::Date(value) => {
                let format = format_description!("[year]-[month]-[day]");
                format!("{}", value.format(format).unwrap())
            }
            QueryExprRightValue::Time(value) => {
                let format = format_description!("[hour]:[minute]:[second]");
                format!("{}", value.format(format).unwrap())
            }
            QueryExprRightValue::DateTime(value) => {
                let format = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
                format!("{}", value.format(format).unwrap())
            }
            QueryExprRightValue::Null => "NULL".into(),
            QueryExprRightValue::Bool(value) => {
                if *value {
                    "TRUE".into()
                } else {
                    "FALSE".into()
                }
            }
            QueryExprRightValue::SliceDate(value) => {
                let format = format_description!("[year]-[month]-[day]");
                format!(
                    "({})",
                    value
                        .iter()
                        .map(|item| item.format(format).unwrap())
                        .collect::<Vec<String>>()
                        .join(",")
                )
            }
            QueryExprRightValue::SliceTime(value) => {
                let format = format_description!("[hour]-[minute]-[second]");
                format!(
                    "({})",
                    value
                        .iter()
                        .map(|item| item.format(format).unwrap())
                        .collect::<Vec<String>>()
                        .join(",")
                )
            }
        }
    }
}

/// 查询表达式
///
/// 支持+、&、|三种运算，其中:
/// * +、& 均将两个Expr合并为一个Expr::And
/// * | 将两个Expr合并为一个Expr::Or
#[derive(Debug)]
pub enum QueryExpr<'a> {
    /// And关系
    And {
        left: Box<QueryExpr<'a>>,
        right: Box<QueryExpr<'a>>,
    },
    /// Or关系
    Or {
        left: Box<QueryExpr<'a>>,
        right: Box<QueryExpr<'a>>,
    },
    /// 单个表达式
    Item {
        field_index: u8,
        right: QueryExprRightValue<'a>,
        /// 查询操作
        method: QueryMethod,
    },
    /// not关系
    Not { query: Box<QueryExpr<'a>> },
    /// join 其他表查询条件
    Join {
        /// join字段的序号
        field_index: u8,
        /// 是否left join
        is_left_join: bool,
        /// 被join表的查询条件
        join_query: Box<QueryExpr<'a>>,
    },
}
// 加法与and一致
impl<'a> Add for QueryExpr<'_> {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self::And {
            left: self.into(),
            right: rhs.into(),
        }
    }
}

impl<'a> BitAnd for QueryExpr<'_> {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self::Output {
        Self::And {
            left: self.into(),
            right: rhs.into(),
        }
    }
}

impl<'a> BitOr for QueryExpr<'_> {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self::Output {
        Self::Or {
            left: self.into(),
            right: rhs.into(),
        }
    }
}

impl<'a> QueryExpr<'a> {
    /// 新建单个表达式
    ///
    /// # 参数说明
    ///
    /// * field_index 表字段索引 用于读取字段信息
    /// * right 表达式的右边部分
    /// * method 查询方法
    pub fn new_item<T: Into<QueryExprRightValue<'a>>>(
        field_index: u8,
        right: T,
        method: QueryMethod,
    ) -> Self {
        Self::Item {
            field_index,
            right: right.into(),
            method,
        }
    }
    /// 新建join表达式
    ///
    /// # 参数说明
    ///
    /// * field_index 表字段索引 用于读取字段信息
    /// * right 表达式的右边部分
    /// * method 查询方法
    /// * join 连接表格的相关查询条件
    pub fn new_item_with_join(field_index: u8, left_join: bool, join_query: QueryExpr<'a>) -> Self {
        Self::Join {
            field_index,
            is_left_join: left_join,
            join_query: join_query.into(),
        }
    }
}

/// 表达式的左边部分的解析信息
#[derive(Debug)]
pub struct LeftExprInfo {
    pub field: Field,
    pub field_sql: String,
    pub joins: Vec<JoinInfo>,
    pub method: QueryMethod,
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_expr() {
        let expr_1 = QueryExpr::new_item(1, "hy", QueryMethod::IExact);
        let expr_2 = QueryExpr::new_item(0, 3, QueryMethod::Exact);
        let expr_3 = expr_1 + expr_2;

        let expr_4 = QueryExpr::new_item(0, [1, 2, 3].as_slice(), QueryMethod::In);
        let last = expr_3 | expr_4;
        dbg!(last);
    }
    /// 测试右表达式的sql
    #[test]
    fn test_right_expr() {
        let u8_expr: QueryExprRightValue = 8u8.into();
        let u16_expr: QueryExprRightValue = 8u16.into();
        let u32_expr: QueryExprRightValue = 8u32.into();
        let u64_expr: QueryExprRightValue = 8u64.into();
        let i8_expr: QueryExprRightValue = (-8i8).into();
        let i16_expr: QueryExprRightValue = (-8i16).into();
        let i32_expr: QueryExprRightValue = (-8i32).into();
        let i64_expr: QueryExprRightValue = (-8i64).into();
        let f32_expr: QueryExprRightValue = 8.3f32.into();
        let f64_expr: QueryExprRightValue = 8.3f64.into();
        let str_expr: QueryExprRightValue = "hello".into();
        let char_expr: QueryExprRightValue = 'r'.into();
        let slice_str_expr: QueryExprRightValue = ["hello", "world"].as_slice().into();
        let slice_char_expr: QueryExprRightValue = ['r', 'u', 's', 't'].as_slice().into();
        let slice_u8_expr: QueryExprRightValue = [1u8, 2u8, 3u8].as_slice().into();
        let slice_u16_expr: QueryExprRightValue = [1u16, 2u16, 3u16].as_slice().into();
        let slice_u32_expr: QueryExprRightValue = [1u32, 2u32, 3u32].as_slice().into();
        let slice_u64_expr: QueryExprRightValue = [1u64, 2u64, 3u64].as_slice().into();
        let slice_i8_expr: QueryExprRightValue = [1i8, 2i8, 3i8].as_slice().into();
        let slice_i16_expr: QueryExprRightValue = [1i16, 2i16, 3i16].as_slice().into();
        let slice_i32_expr: QueryExprRightValue = [1i32, 2i32, 3i32].as_slice().into();
        let slice_i64_expr: QueryExprRightValue = [1i64, 2i64, 3i64].as_slice().into();
        let slice_f32_expr: QueryExprRightValue = [1f32, 2f32, 3f32].as_slice().into();
        let slice_f64_expr: QueryExprRightValue = [1f64, 2f64, 3f64].as_slice().into();
        assert_eq!(u8_expr.to_sql(), "8");
        assert_eq!(u16_expr.to_sql(), "8");
        assert_eq!(u32_expr.to_sql(), "8");
        assert_eq!(u64_expr.to_sql(), "8");
        assert_eq!(i8_expr.to_sql(), "-8");
        assert_eq!(i16_expr.to_sql(), "-8");
        assert_eq!(i32_expr.to_sql(), "-8");
        assert_eq!(i64_expr.to_sql(), "-8");
        assert_eq!(f32_expr.to_sql(), "8.3");
        assert_eq!(f64_expr.to_sql(), "8.3");
        assert_eq!(str_expr.to_sql(), "'hello'");
        assert_eq!(char_expr.to_sql(), "'r'");
        assert_eq!(slice_str_expr.to_sql(), "('hello','world')");
        assert_eq!(slice_char_expr.to_sql(), "('r','u','s','t')");
        assert_eq!(slice_u8_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_u16_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_u32_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_u64_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_i8_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_i16_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_i32_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_i64_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_f32_expr.to_sql(), "(1,2,3)");
        assert_eq!(slice_f64_expr.to_sql(), "(1,2,3)");
    }
}
